#include <stdio.h>
#include "key.h"
#include "systick.h"
#include "i2c.h"
#include "hal_i2c.h"
#include "clock_init.h"

#define APP_I2C_TARGET_ADDR 0x50u  /* I2C target device address. */
static I2C_MasterXfer_Type app_i2c_xfer;
static uint8_t app_i2c_rx_buf[20];  /* I2C rx buffer. */
static uint8_t app_i2c_tx_buf[20];  /* I2C tx buffer. */
volatile static bool app_i2c_xfer_done;          /* Xfer done flag. */

void BOARD_I2C1_Init(void)
{
    GPIO_Init_Type gpio_init;

    /* GPIOC. */
    RCC_EnableAHB1Periphs(RCC_AHB1_PERIPH_GPIOC, true);
    RCC_ResetAHB1Periphs(RCC_AHB1_PERIPH_GPIOC);

    /* I2C1 */
    RCC_EnableAPB1Periphs(RCC_APB1_PERIPH_I2C1, true);
    RCC_ResetAPB1Periphs(RCC_APB1_PERIPH_I2C1);

    /* PC6 - I2C1_SCL. */
    gpio_init.Pins  = GPIO_PIN_6;
    gpio_init.PinMode  = GPIO_PinMode_AF_OpenDrain;
    gpio_init.Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOC, &gpio_init);
    GPIO_PinAFConf(GPIOC, gpio_init.Pins, GPIO_AF_4);

    /* PC7 - I2C1_SDA. */
    gpio_init.Pins  = GPIO_PIN_7;
    gpio_init.PinMode  = GPIO_PinMode_AF_OpenDrain;
    gpio_init.Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOC, &gpio_init);
    GPIO_PinAFConf(GPIOC, gpio_init.Pins, GPIO_AF_4);

    /* Setup I2C initialization values. */
    I2C_Master_Init_Type i2c_init;
    i2c_init.ClockFreqHz = CLOCK_APB1_FREQ;
    i2c_init.BaudRate = I2C_BaudRate_100K;
    app_i2c_xfer.TargetAddr = APP_I2C_TARGET_ADDR;

    /* Initialize I2C master. */
    I2C_InitMaster(I2C1, &i2c_init);

    /* The target device address needs to be configured before enabling. */
    I2C_SetTargetAddr(I2C1, app_i2c_xfer.TargetAddr);

    /* Enable I2C. */
    I2C_Enable(I2C1, true);
    NVIC_EnableIRQ(I2C1_IRQn);
}

/* When I2C tx done, use app_i2c_tx_done_callback. */
void app_i2c_tx_done_callback(void *param)
{
    printf("write data: ");
    for (uint32_t i = 0u; i < sizeof(app_i2c_tx_buf); i++)
    {
        printf("0x%02X, ", app_i2c_tx_buf[i]);
    }
    printf("\r\n");
    app_i2c_xfer_done = true;
}

/* When I2C tx abort, use app_i2c_tx_abort_callback. */
void app_i2c_tx_abort_callback(void *param)
{
    printf("tx_abort\r\n");
    app_i2c_xfer_done = true;
}

/* When I2C received done, use app_i2c_rx_done_callback. */
void app_i2c_rx_done_callback(void *param)
{
    printf("read data from device register address: 0x%02X\r\n", app_i2c_tx_buf[0u]);
    printf("read data:  ");
    for (uint32_t i = 0u; i < sizeof(app_i2c_rx_buf); i++)
    {
        printf("0x%02X, ", app_i2c_rx_buf[i]);
    }
    printf("\r\n");
    app_i2c_xfer_done = true;
}

/* When I2C received abort, use app_i2c_rx_abort_callback. */
void app_i2c_rx_abort_callback(void *param)
{
    printf("rx_abort\r\n");
    app_i2c_xfer_done = true;
}

/* Put data to device. */
void app_i2c_write(void)
{
    /* Setup I2C tx. */
    app_i2c_xfer.Direction = I2C_Direction_Tx;                 /* Setup the xfer direction. */
    app_i2c_xfer.TxBuf = app_i2c_tx_buf;                       /* Setup tx buffer. */
    app_i2c_xfer.TxLen = sizeof(app_i2c_tx_buf);               /* Setup the number of data tx. */

    app_i2c_xfer.DoneCallback = app_i2c_tx_done_callback;      /* Transmission abort callback. */
    app_i2c_xfer.AbortCallback = app_i2c_tx_abort_callback;    /* Transmission done callback. */
    app_i2c_xfer_done = false;                                 /* Setup xfer done flag status. */

    I2C_MasterXfer(I2C1, &app_i2c_xfer);             /* I2C xfer. */

    while (false == app_i2c_xfer_done)                         /* Wait to xfer done. */
    {
    }
}

/* Get data from device. */
void app_i2c_read(void)
{
    app_i2c_xfer.Direction  = I2C_Direction_Rx;         /* Setup the xfer direction. */
    app_i2c_xfer.TxBuf = app_i2c_tx_buf;                /* Setup tx buffer. */
    app_i2c_xfer.RxBuf = app_i2c_rx_buf;                /* Setup rx buffer. */
    app_i2c_xfer.TxLen = 1;                             /* One data needs to be write to the target address before reading operation. */
    app_i2c_xfer.RxLen = sizeof(app_i2c_rx_buf);                /* Setup the number of receive data. */
    app_i2c_xfer.DoneCallback = app_i2c_rx_done_callback;    /* Transmission abort callback. */
    app_i2c_xfer.AbortCallback = app_i2c_rx_abort_callback;  /* Transmission done callback. */
    app_i2c_xfer_done = false;                               /* Setup xfer done flag status. */

    I2C_MasterXfer(I2C1, &app_i2c_xfer);           /* I2C xfer. */

    while (false == app_i2c_xfer_done)  /* Waiting for xfer done. */
    {
    }
}

void I2C_InterruptTest(void)
{
    BOARD_I2C1_Init();

    for (uint32_t i = 0u; i < 20; i++)   /* Setup the data to be transmitted. */
    {
        app_i2c_tx_buf[i] = i;
    }

    app_i2c_write();  /* I2C write data to target device. */
    BOARD_Delay1Ms(10u);   /* Wait for i2c xfer finish. */

    app_i2c_read();  /* I2C read data from target device. */
    BOARD_Delay1Ms(10u);  /* Wait for i2c xfer finish. */

    while(1)
    {

    }
}

/* I2C interrupt handler */
void I2C1_IRQHandler(void)
{
    uint32_t flag = I2C_GetInterruptStatus(I2C1);      /* Get current interrupt status. */
    I2C_ClearInterruptStatus(I2C1, flag);              /* Clear the interrupt flag that can be cleared by software. */
    I2C_MasterXferHandler(I2C1, &app_i2c_xfer, flag);  /* I2C master xfer interrupt handler. */
}
